package com.wx.elasticsearch.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.wx.elasticsearch.dao.EsProductDao;
import com.wx.elasticsearch.pojo.EsProduct;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author wangxun
 */
@Slf4j
@Service
public class EsProductService {

    @Autowired
    private EsProductDao esProductDao;

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    public boolean createIndex(String indexName) {
//        return elasticsearchRestTemplate.createIndex(indexName);
        return elasticsearchRestTemplate.createIndex(EsProduct.class);
    }

    public boolean indexExists(String indexName) {
        // 下面两者是等价的
//        return elasticsearchRestTemplate.indexExists(indexName);
        return elasticsearchRestTemplate.indexExists(EsProduct.class);
    }

    public boolean deleteIndex(String indexName) {
        // 下面两者是等价的
//        return elasticsearchRestTemplate.deleteIndex(indexName);
        return elasticsearchRestTemplate.deleteIndex(EsProduct.class);
    }

    public boolean createMapping() {
        // 下面两者是等价的
//        return elasticsearchRestTemplate.putMapping("pms", "product", EsProduct.class);
        return elasticsearchRestTemplate.putMapping(EsProduct.class);
    }

    public Map<String, Object> getMapping(String indexName, String type) {
        // 下面两者是等价的
//         return elasticsearchRestTemplate.getMapping(indexName, type);
         return elasticsearchRestTemplate.getMapping(EsProduct.class);
    }

    public String createDocument(Long id) {
        EsProduct esProduct = esProductDao.getById(id);

        IndexQuery indexQuery = new IndexQuery();
        indexQuery.setIndexName("pms");
        indexQuery.setType("product");
        indexQuery.setId(esProduct.getId().toString());
        indexQuery.setObject(esProduct);

        return elasticsearchRestTemplate.index(indexQuery);
    }

    public boolean updateDocument(Long id) {
        EsProduct esProduct = esProductDao.getById(id);
        esProduct.setKeywords(esProduct.getKeywords() + " Update");
        HashMap<Object, Object> esProductMap = CollectionUtil.newHashMap();
        esProductMap.put("keywords", esProduct.getKeywords());

        UpdateQuery query = new UpdateQuery();
        query.setIndexName("pms");
        query.setType("product");
        query.setId(id.toString());
        query.setDoUpsert(true);
        UpdateRequest updateRequest = new UpdateRequest();

        updateRequest.doc(esProductMap);
        query.setUpdateRequest(updateRequest);

        UpdateResponse result = elasticsearchRestTemplate.update(query);
        log.info("updateDocument: {}", result.toString());

        return ObjectUtil.isNotNull(result);
    }

    public String deleteDocument(Long id) {
        return elasticsearchRestTemplate.delete(EsProduct.class, id.toString());
    }

    public boolean batchCreateDocument() {
        Collection<EsProduct> allProduct = esProductDao.getAll();

        List<IndexQuery> queries = allProduct.stream()
                .map(product -> new IndexQueryBuilder().withIndexName("pms").withType("product").withId(product.getId().toString()).withObject(product).build())
                .collect(Collectors.toList());

        elasticsearchRestTemplate.bulkIndex(queries);
        return true;
    }

    public EsProduct getById(Long id) {
        GetQuery query = GetQuery.getById(id.toString());
        return elasticsearchRestTemplate.queryForObject(query, EsProduct.class);
    }

    /**
     * select * from product limit ?, ?
     * from=?&size=? 分页查询
     */
    public List<EsProduct> getByPage(Integer pageNum, Integer pageSize) {
        Pageable pageable = PageRequest.of(pageNum, pageSize);
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        builder.withPageable(pageable);

        return elasticsearchRestTemplate.queryForList(builder.build(), EsProduct.class);
    }

    public List<EsProduct> getByIds(String ids) {
        String[] strIds = ids.split(",");
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        builder.withIds(CollectionUtil.toList(strIds));
        return elasticsearchRestTemplate.multiGet(builder.build(), EsProduct.class);
    }

    /**
     * select * from product where brandName = ? and productCategoryName = ?
     * bool must term 精确匹配
     */
    public List<EsProduct> getByBrandNameAndProductCategoryName(String brandName, String productCategoryName) {
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.termQuery("brandName", brandName));
        boolQueryBuilder.must(QueryBuilders.termQuery("productCategoryName", productCategoryName));

        builder.withFilter(boolQueryBuilder);

        return elasticsearchRestTemplate.queryForList(builder.build(), EsProduct.class);
    }

    /**
     * select * from product where brandName = ? or productCategoryName = ?
     * bool should term 精确匹配
     */
    public List<EsProduct> getByBrandNameOrProductCategoryName(String brandName, String productCategoryName) {
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.should(QueryBuilders.termQuery("brandName", brandName));
        boolQueryBuilder.should(QueryBuilders.termQuery("productCategoryName", productCategoryName));
//        boolQueryBuilder.minimumShouldMatch()
        builder.withFilter(boolQueryBuilder);

        return elasticsearchRestTemplate.queryForList(builder.build(), EsProduct.class);
    }

    /**
     * select * from product where brandName = ? or productCategoryName = ?
     * bool should term 精确匹配
     */
    public List<EsProduct> likeKeywordAndName(String keyword, String name) {
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();

        List<FunctionScoreQueryBuilder.FilterFunctionBuilder> filterFunctionBuilders = new ArrayList<>();
        filterFunctionBuilders.add(
                new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("keyword", keyword), ScoreFunctionBuilders.weightFactorFunction(10))
        );
        filterFunctionBuilders.add(
                new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("name", name), ScoreFunctionBuilders.weightFactorFunction(2))
        );

        FunctionScoreQueryBuilder.FilterFunctionBuilder[] builders = new FunctionScoreQueryBuilder.FilterFunctionBuilder[filterFunctionBuilders.size()];
        filterFunctionBuilders.toArray(builders);
        FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(builders)
                .scoreMode(FunctionScoreQuery.ScoreMode.SUM)
                .setMinScore(2);

        builder.withQuery(functionScoreQueryBuilder);

        return elasticsearchRestTemplate.queryForList(builder.build(), EsProduct.class);
    }
}
